﻿/**
 *	\file		archive_file_filter.hpp
 *	\date		(2009-01-25 14:04:49)/(2014-12-20 15:41:39)
 *-----------------------------------------------------------------------------
 *	\version	1.0.7.14
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#if			!defined(__LOLIX_TOY__ARCHIVE_FILE_FILTER__NICK__K_IN_G__)
#define		__LOLIX_TOY__ARCHIVE_FILE_FILTER__NICK__K_IN_G__
//==============================================================================
//--------------------------------------------------------------------------
#if			!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)
	#include	"../loli/file.h"
#endif	//	!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)
//--------------------------------------------------------------------------
//==============================================================================
#endif	//	!defined(__LOLIX_TOY__ARCHIVE_FILE_FILTER__NICK__K_IN_G__)


#if			defined(LOLIX_DEF__MEMORY_FILE_STREAM) && !defined(__LOLIX__ALREADY_DEFINE_MEMORY_FILE_STRAEM__)
#define		__LOLIX__ALREADY_DEFINE_MEMORY_FILE_STRAEM__
	#if			!defined(__LOLIX__TOY__STDC_FUN__NICK__K_IN_G__)
		#include	"./stdc_fun.h"
	#endif	//	!defined(__LOLIX__TOY__STDC_FUN__NICK__K_IN_G__)
	#if			!defined(__LOLIX__TOY__STD_VECTOR__NICK__K_IN_G__)
		#include	"./std_vector.hpp"
	#endif	//	!defined(__LOLIX__TOY__STD_VECTOR__NICK__K_IN_G__)

namespace	lolix{namespace loli{
	template<typename _TAlc>
	class	memory_file_stream;
}}


template<typename _TAlc>
class	lolix::loli::memory_file_stream
	: public file_stream
{
	CANNT_COPY_LOLI;
	typedef	toy::vector<lx_u8, _TAlc>	_Data_type;
	_Data_type	_dat;
	size_type	_cur;
	size_type	_total;
	toy::stdc_fun*	_stdc;

	void* operator new(size_t);
	void operator delete(void*);
public:
	typedef _Data_type data_type;
#if			LOLIX_CFG__SUPPORT_CPP_0X
	LOLIX_INLINE_CALL	memory_file_stream(memory_file_stream&& mfs)
		: _dat(mfs._dat.get_allocator()), _cur(mfs._cur), _stdc(mfs._stdc)
		, _total(mfs._total)
	{
		_dat.swap(mfs._dat);
		mfs._stdc = 0;
	}

#endif	//	LOLIX_CFG__SUPPORT_CPP_0X

	LOLIX_INLINE_CALL	memory_file_stream(toy::stdc_fun* stdc)
		: _cur(0), _stdc(stdc), _total(0){_stdc->inc_ref();}
	explicit LOLIX_INLINE_CALL	memory_file_stream(toy::stdc_fun* stdc, _TAlc alc)
		: _cur(0), _stdc(stdc), _dat(alc), _total(0){_stdc->inc_ref();}
	explicit LOLIX_INLINE_CALL	memory_file_stream(data_type& dat, toy::stdc_fun* stdc)
		: _cur(0), _dat(dat), _total(_dat.size()){_stdc->inc_ref();}
	LOLIX_INLINE_CALL ~memory_file_stream(void){ _stdc->safe_release(); }

	LOLIX_INTERFACE_DEFINE(inc_ref		, size_type		, (void)const)
	{
		return 2;
	}
	LOLIX_INTERFACE_DEFINE(release		, size_type		, (void)const)
	{
		return 0;
	}
	LOLIX_INTERFACE_DEFINE(itf_id		, void			, (LOLIX_ITF_ID* outid)const)
	{
		LOLIX_ASSERT(0, "UNSUPPORT INTERFACE");
	}
	LOLIX_INTERFACE_DEFINE(query_itf	, LOLIX_RETNV	, (itf** out_ptr, const LOLIX_ITF_ID* in_id))
	{
		if ( !LOLIX_ITF_ID_EQUL_WITH_TYPE(in_id, file_stream) )
			return LOLIX_ERROR_INVALID_TYPE;

		*out_ptr = this;
		return LOLIX_RETNV_OK;
	}
	LOLIX_INTERFACE_DEFINE(size			, lx_u64		, (void)const)
	{
		return _total;
	}

	LOLIX_INTERFACE_DEFINE(close		, LOLIX_RETNV	, (void))
	{
		_dat.clear();
		_cur = 0;
		_total = 0;
		return LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEFINE(opt_flag		, void	, (open_flag* const of)const)
	{
		of->flag = 0;
		of->operat_read = 1;
		of->operat_write = 1;
		of->type_mask = of->create_new;
		of->with_flow = 1;
		of->spt_bidir = 1;
	}

	LOLIX_INTERFACE_DEFINE(get_name	, LOLIX_RETNV	, (wchar_t full_name[], size_type* const io_file_name_len)const)
	{
		return LOLIX_ERROR_UNSUPPORT_INTERFACE;
	}
	LOLIX_INTERFACE_DEFINE(remove		, LOLIX_RETNV	, (void))
	{
		return LOLIX_ERROR_UNSUPPORT_INTERFACE;
	}
	//	new_offset: [in] need offset, [out] new offset >> distance of begining
	//	int : 0 -> from header, 1 -> from current, 2 -> from end
	LOLIX_INTERFACE_DEFINE(seek	, LOLIX_RETNV	, (lx_i64* new_offset, int start))
	{
		if ( *new_offset > 0 && static_cast<lx_u64>(*new_offset) > type::type_value<decltype(_cur)>::max_val
			|| *new_offset < 0 && static_cast<lx_u64>(-*new_offset) > type::type_value<decltype(_cur)>::max_val )
				return LOLIX_ERROR_OUT_OF_RANGE;

		LOLIX_ASSERT(start < 2 || *new_offset <= 0, L"offset must less or equal than zero");

		switch ( start )
		{
		case 0:
			_cur = (size_type)*new_offset;
			break;

		case 1:
			if ( *new_offset > 0 && type::type_value<decltype(_cur)>::max_val - _cur < (size_type)*new_offset
				|| *new_offset < 0 && _cur < ((size_type)-*new_offset) )
				return LOLIX_ERROR_OUT_OF_RANGE;

			_cur += (size_type)*new_offset;
			*new_offset = _cur;
			break;

		case 2:
			if ( static_cast<size_type>(-*new_offset) > _total )
				return LOLIX_ERROR_OUT_OF_RANGE;

			*new_offset = _total + *new_offset;
			break;

		default:
			return LOLIX_ERROR_INVALID_PARAM;
		}

		return LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEFINE(touch, LOLIX_RETNV	, (void* buf, size_type* buf_size))
	{
		if ( !_stdc )
			return LOLIX_ERROR_INVALID_STATE;

		if ( _cur >= _total )
		{
			*buf_size = 0;
			return LOLIX_RETNV_OK;
		}

		const size_type less_size = _total - _cur;
		const size_type cp_size = (*buf_size < less_size)?*buf_size:less_size;
		_stdc->memcpy(buf, &*_dat.begin() + _cur, cp_size);
		*buf_size = cp_size;
		return LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEFINE(read	, LOLIX_RETNV	, (void* buf, size_type* buf_size))
	{
		LOLIX_RETNV rtv;
		if ( LOLIX_IS_ERROR(rtv = touch(buf, buf_size)) )
			return rtv;
		_cur += *buf_size;
		return LOLIX_RETNV_OK;
	}
	LOLIX_INTERFACE_DEFINE(write, LOLIX_RETNV	, (const void* buf	, size_type* buf_size))
	{
		if ( !_stdc )
			return LOLIX_ERROR_INVALID_STATE;

		const size_type total_size = _cur + *buf_size;
		if ( total_size < _cur )
			return LOLIX_ERROR_OUT_OF_RANGE;

		if ( _total < total_size )
		{
			_total = total_size;
			if ( _total > _dat.size() )
				_dat.resize(total_size);
		}

		_stdc->memcpy(&*_dat.begin() + _cur, buf, *buf_size);
		_cur += *buf_size;
		return LOLIX_RETNV_OK;
	}

	LOLIX_INLINE_CALL const void* dates(void)
	{
		return &_dat.front();
	}

	LOLIX_INLINE_CALL void swap_datas(data_type& dat, size_type total_size)
	{
		this->_dat.swap(dat);
		_total = total_size;
	}
};


#endif	//	defined(LOLIX_DEF__MEMORY_FILE_STREAM) && !defined(__LOLIX__ALREADY_DEFINE_MEMORY_FILE_STRAEM__)


#if			defined(LOLIX_DEF__MANAGED_FILE_STREAM) && !defined(__LOLIX__ALREADY_DEFINE_MANAGED_FILE_STREAM__) && defined(__cplusplus_cli)
#define		__LOLIX__ALREADY_DEFINE_MANAGED_FILE_STREAM__

namespace lolix{namespace loli{class managed_file_flow;}}

class	lolix::loli::managed_file_flow
	: public lolix::loli::file_flow
{
	LOLIX_DEFINE_GLOBAL_ITF(lolix::loli::managed_file_flow, lolix::loli::file_flow, (0xea4826f1, 0x41824479, 0x92a34df8, 0x1843c501));
	gcroot<System::IO::Stream^> _file;
	gcroot<array<unsigned char>^> _rdbuf;
	bool	_need_del;
public:
	using base_type::read;
	using base_type::write;
	using base_type::touch;

	LOLIX_INLINE_CALL managed_file_flow(System::IO::Stream^ s, bool selfDelete)
		: _file(s)
		, _need_del(selfDelete)
	{ }

	LOLIX_INLINE_CALL ~managed_file_flow(void)
	{
		if ( _need_del )
			delete _file;
	}

	LOLIX_INTERFACE_DEF(size		, lx_u64		, (void)const)
	{
		try
		{
			return _file->Length;
		}
		catch(System::NotSupportedException^)
		{
			return ~0ul;
		}
	}

	LOLIX_INTERFACE_DEF(close		, LOLIX_RETNV	, (void))
	{
		_file->Close();
		return LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEF(opt_flag	, void			, (open_flag* const out)const)
	{
		out->flag = 0;
		out->with_flow = 1;
		out->spt_bidir = _file->CanSeek;
		out->operat_read = _file->CanRead;
		out->operat_write = _file->CanWrite;
	}

	LOLIX_INTERFACE_DEF(get_name	, LOLIX_RETNV	, (wchar_t /*full_name*/[], size_type* const /*io_file_name_len*/)const)
	{
		return LOLIX_ERROR_UNSUPPORT_INTERFACE;
	}

	LOLIX_INTERFACE_DEF(remove		, LOLIX_RETNV	, (void))
	{
		return LOLIX_ERROR_UNSUPPORT_INTERFACE;
	}

	LOLIX_INTERFACE_DEF(touch		, LOLIX_RETNV	, (void* buf, size_type* buf_size))
	{
		LOLIX_RETNV rtv;
		if ( _file->CanSeek )
		{
			if ( !LOLIX_IS_ERROR(rtv = read(buf, buf_size)) )
				_file->Seek(-(lx_i64)*buf_size, System::IO::SeekOrigin::Current);
			return rtv;
		}

		char* cbuf = static_cast<char*>(buf);
		if ( !_rdbuf )
		{
			if ( LOLIX_IS_ERROR(rtv = read(buf, buf_size)) )
				return rtv;
			_rdbuf = gcnew array<unsigned char>(static_cast<int>(*buf_size));
			pin_ptr<unsigned char> pbuf = &_rdbuf[0];
			__Sample_copy(pbuf, cbuf, _rdbuf->Length);
			return rtv;
		}

		pin_ptr<unsigned char> pbuf = &_rdbuf[0];
		if ( (size_type)_rdbuf->Length >= *buf_size )
		{
			__Sample_copy(cbuf, pbuf, *buf_size);
			return LOLIX_RETNV_OK;
		}

		*buf_size -= _rdbuf->Length;
		if ( !LOLIX_IS_ERROR(rtv = read(cbuf + _rdbuf->Length, buf_size)) )
			return rtv;
		__Sample_copy(cbuf, pbuf, _rdbuf->Length);
		*buf_size += _rdbuf->Length;
		_rdbuf = gcnew array<unsigned char>(static_cast<int>(*buf_size));
		pbuf = &_rdbuf[0];
		__Sample_copy(pbuf, buf, *buf_size);
		return lolix::LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEF(read		, LOLIX_RETNV	, (void* buf, size_type* buf_size))
	{
		char* const cbuf = static_cast<char*>(buf);
		size_type need_read = *buf_size;
		if ( _file->CanSeek || !_rdbuf )
			return __Read_direct_last(cbuf, buf_size, need_read);

		pin_ptr<unsigned char> pbuf = &_rdbuf[0];
		if ( (size_type)_rdbuf->Length <= *buf_size )
		{
			__Sample_copy(cbuf + *buf_size - need_read, pbuf, _rdbuf->Length);
			_rdbuf = 0;
			need_read -= _rdbuf->Length;
			return __Read_direct_last(cbuf, buf_size, need_read);
		}

		__Sample_copy(cbuf, pbuf, *buf_size);
		array<unsigned char>^ nbuf = gcnew array<unsigned char>(static_cast<int>(_rdbuf->Length - *buf_size));
		_rdbuf->CopyTo(nbuf, static_cast<int>(*buf_size));
		_rdbuf = nbuf;
		return LOLIX_RETNV_OK;
	}

	LOLIX_INTERFACE_DEF(write		, LOLIX_RETNV	, (const void* buf	, size_type* buf_size))
	{
		if ( !_file->CanSeek && _rdbuf )
			return LOLIX_ERROR_OUT_OF_SYNC;

		char const* const cbuf = static_cast<const char*>(buf);
		array<unsigned char>^ wtbuf = gcnew array<unsigned char>(KB(4));
		size_type need_write = *buf_size;
		while ( need_write )
		{
			int cur_write_size = wtbuf->Length;
			if ( static_cast<size_type>(cur_write_size) > need_write )
				cur_write_size = static_cast<int>(need_write);
			pin_ptr<unsigned char> wtcatch = &wtbuf[0];
			__Sample_copy(wtcatch, cbuf + *buf_size - need_write, cur_write_size);
			_file->Write(wtbuf, 0, cur_write_size);
			need_write -= cur_write_size;
		}

		*buf_size -= need_write;
		delete wtbuf;
		return LOLIX_RETNV_OK;
	}

private:
	void __Sample_copy(void* s, const void* d, size_type i)
	{
		char* cs = static_cast<char*>(s);
		const char* cd = static_cast<const char*>(d);
		for ( size_type k = 0; k != i; ++k )
			cs[k] = cd[k];
	}

	LOLIX_RETNV __Read_direct_last(char* const cbuf, size_type* buf_size, size_type need_read)
	{
		array<unsigned char>^ rdbuf = gcnew array<unsigned char>(KB(4));
		while ( need_read )
		{
			int cur_read_size = rdbuf->Length;
			if ( static_cast<size_type>(cur_read_size) > need_read )
				cur_read_size = static_cast<int>(need_read);
			int rdlen = _file->Read(rdbuf, 0, cur_read_size);
			if ( !rdlen )
				break;
			pin_ptr<unsigned char> rdcatch = &rdbuf[0];
			__Sample_copy(cbuf + *buf_size - need_read, rdcatch, rdlen);
			need_read -= rdlen;
		}
		*buf_size -= need_read;
		delete rdbuf;
		return LOLIX_RETNV_OK;
	}
};


#endif	//	defined(LOLIX_DEF__MANAGED_FILE_STREAM) && !defined(__LOLIX__ALREADY_DEFINE_MANAGED_FILE_STREAM__) && defined(__cplusplus_cli)
